#! /usr/bin/python
# -*- coding: utf-8 -*-

"""ftp class
"""

import sys,os
import wx
import random
import StringIO,string
import ftplib
import time
from ftplib import FTP
from multiprocessing import Process, Queue, Manager
import threading, thread
import os

def parseDir(dirList):
    li = dirList.split('\n')[0:-1]
    lq = [(string.split(w,maxsplit=8)[0][0]=='d',
            string.split(w,maxsplit=8)[-1],
            string.split(w,maxsplit=8)[4]) for w in li]
    ll = filter(lambda x: x[1] != '.' and x[1] != '..', lq)
    lr = ('is_dir', 'name', 'size')
    return [dict(zip(lr,w)) for w in ll]
    
    
class Log:
    output = sys.stderr
    def __init__(self, ot):
        self.output = ot
    def set(self, ot):
        self.output = ot
    def log(self, message, forColor="DARK GREEN", backColor="WHITE"):
        f = wx.Font(12, wx.ROMAN,wx.FONTSTYLE_NORMAL, wx.BOLD)
        self.output.SetStyle(sys.maxint,sys.maxint, wx.TextAttr(forColor, backColor,f))        
        print >> self.output, message

class DisconnectException(Exception):
    pass
class Qftp:
    lock = thread.allocate_lock()
    def __init__(self, addr, port, user, password, encoding):
        self.addr = addr
        self.port = port
        self.user = user
        self.password = password
        self.log = Log(sys.stderr)
        self.ftp = FTP()
        self.STOP_FLAG = False
        self.CONNECTION_BUILT = False
        self.DOWNLOAD_FINISHED = False
        self.download_dir = os.getcwd()
        os.chdir(self.download_dir)
        self.cnt = 0
#        self.ns.ftp.set_pasv(True)
#        self.ftp.set_debuglevel(1)
        self.encoding = encoding
        self.StopConnecting = False
        self.DOWNLOADING = []
        self.DOWNLOADED = []
    
    def setlog(self, textctrl):
        self.log.set(textctrl)
    
    def stopConnect(self):
        self.STOP_FLAG = True
        self.log.log('We are now trying to disconnect...')
    def close(self):
        self.ftp.close()    
    # keep connecting until connection built
    def connect(self):
        while(True):
            if self.STOP_FLAG is True:
                raise DisconnectException
            try: self.ftp.connect(self.addr, self.port)
            except ftplib.all_errors, e:
                self.log.log(e)
                self.ftp.close()
                randn = random.randint(120,130)
                self.log.log('we have to wait for %d seconds before reconnect:   '%randn)
                for i in xrange(1, randn):
                    start = self.log.output.GetInsertionPoint()
                    self.log.log('timer count: %3d'%i)
                    end = self.log.output.GetInsertionPoint()
                    if self.STOP_FLAG is True:
                        raise DisconnectException
                    time.sleep(1)
                    self.log.output.Remove(start,end)
                continue
            try: self.ftp.login(self.user, self.password)
            except ftplib.all_errors, e:
                self.log.log(e)
                if self.cnt == 0:
                    start = self.log.output.GetInsertionPoint()
                    self.log.log('we are trying to connect... '+'-')
                    end = self.log.output.GetInsertionPoint()
                if self.cnt%2 == 0:
                    self.log.output.Remove(start,end)
                    start = self.log.output.GetInsertionPoint()
                    self.log.log('we are trying to connect... '+'-')
                    end = self.log.output.GetInsertionPoint()
                else:
                    self.log.output.Remove(start,end)
                    start = self.log.output.GetInsertionPoint()
                    self.log.log('we are trying to connect... '+'|')
                    end = self.log.output.GetInsertionPoint()
                self.cnt+=1
                continue
            self.cnt = 0
            break
        self.CONNECTION_BUILT = True
        self.log.log('connection build...', wx.Colour(1,37,69), wx.Colour(249, 185, 210))
        return
    
    # quit politely
    def quit(self):
        try: self.ftp.quit()
        except Exception,e:
            self.log.log(e)
        self.CONNECTION_BUILT = False
        self.STOP_FLAG = False
        self.cnt = 0
        self.log.log('quit successful...', wx.Colour(1,37,69), wx.Colour(249, 185, 210))
    
    def abort(self):
        self.ftp.abort()
        
    def pwd(self):
        return self.ftp.pwd()
        
    # test if we are still connected to the server
    def has_connected(self):
        try: self.ftp.pwd()
        except Exception, e:
            self.log.log('tt:'+str(e))
            return False
        return True
    
        
    # return a list of dict(is_dir:True/False, name:'name', size:size)
    def list_dir(self, dir):
        self.log.log('listing dir...')
        self.log.log(dir)
        if not self.has_connected():
            self.log.log('connection lost, we need to rebuild')
            self.connect()
        Qftp.lock.acquire()
        output = StringIO.StringIO()
        sys.stdout = output
        try:
            self.ftp.cwd(dir.encode(self.encoding))
        except Exception,e:
            self.log.log(e)
            Qftp.lock.release()
            return None
        self.ftp.dir()
        dirList = output.getvalue()
        output.close()
        sys.stdout = sys.__stdout__
        Qftp.lock.release()
#        self.log.log(dirList)
        return parseDir(dirList.decode(self.encoding))
    
    def download(self, srcdir, dstdir):
        self.DOWNLOAD_FINISHED = False
        basedir = os.path.basename(srcdir)
        dstdir = os.path.join(dstdir, basedir)
        self.download_func(srcdir, dstdir, '100')
        self.log.log('download finished', wx.Colour(1,37,69), wx.Colour(249, 185, 210))
        self.DOWNLOAD_FINISHED = True
        
    # download recursively
    def download_func(self, srcdir, dstdir, size):
#        print 'srcdir:',
#        print srcdir
#        print 'dstdir:',
#        print dstdir
#        print 'size:',
#        print size
        if srcdir == '/': return
        if not self.has_connected():
            self.log.log('connection lost, we need to rebuild')
            self.connect()
        try: self.ftp.cwd(srcdir.encode(self.encoding))
        except Exception,e: #it's a file, not a dir
            self.log.log(e)
            self.log.log('downloading file %s' %os.path.basename(srcdir), wx.Colour(137, 56, 7), wx.Colour(202, 215, 232))
            cmd = 'RETR '+srcdir
            fp = open(dstdir,'wb')
            fileHandler = fp.write
            
            self.DOWNLOADING.append((dstdir, str(long(size)+1)))
            
            try: self.ftp.retrbinary(cmd.encode(self.encoding), fileHandler)
            except Exception,e:
                self.log.log('error occured while downloading %s'%os.path.basename(srcdir))
                self.log.log(e)
                print e
            finally: fp.close()
            
            self.DOWNLOADED.append(self.DOWNLOADING.pop())
            
            return
        if not os.path.exists(dstdir):
            os.mkdir(dstdir)
        os.chdir(dstdir)
        listDir = self.list_dir(srcdir)
        for dic in listDir:
            base = dic['name']
            is_dir = dic['is_dir']
            size = dic['size']
            sdir = srcdir+'/'+base
            ddir = os.path.join(dstdir,base)
            self.download_func(sdir, ddir, size)

if __name__ == '__main__':
    print 'iy'
    pass
#    lftp = Qftp('202.117.54.250', '4021', 'list', 'list', 'gb18030')
#    lftp2 = Qftp('202.117.54.250', '4021', 'movie', 'movie', 'gb18030')
#    lftp.connect_func()
#    lftp.ftp.quit()
#    lftp.connect_func()
#    print lftp.ftp.pwd()
#    p1 = Process(target = lftp.list_dir, args = ('/',))
#    p1.start()
#    p2 = Process(target = lftp2.list_dir, args = ('/',))
#    p2.start()
#    p1.join()
#    p2.join()
#    p.join()
#    print '... ...----------'
#    p2 = Process(target = lftp.list_dir_func, args = ('/',))
#    p2.start()
#    p1.join()
#    p2.join()

#    p = threading.Thread(target = lftp.connect_func)
#    p.start()
#    time.sleep(3)
#    lftp.stop_connecting()
#    p.join()
#    p.start()
#    p.join()
#    time.sleep(3)
#    p = lftp.list_dir('/newb')
#    p.join()
#    while(p.is_alive()):
#        pass
#    p.join()
    #time.sleep(5)
#    print ftp.has_connected()
#    ftp.disconnect()
#    print lftp.has_connected()
#    p = lftp.list_dir('/')
#    lftp.ftp.pwd()
#    p.join()
#    dir = lftp.list_dir_func(unicode('/newb'))
#    lftp.download_func('/')
#    print dir
    #print dir[0]['name']